/**
 * Procesadores de Lenguaje, curso 2009-2010
 * @author grupo 02
 *
 */

package traductor;

import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Hashtable;
import traductor.AnalizadorLexico.CatLexica;

public class AnalizadorSintactico {

    private AnalizadorLexico anLex; //instancia del analizador lexico

    private Hashtable ts; //tabla de simbolos del analizador sintactico
    private int dir; //direccion actual de la tabla de simbolos
    private ArrayList<String> err; //lista de errores
    private String cod; //atributo para ir acumulando el codigo de la maquinaP
    private String codDebug; //atributo para ir acumulando el codigo de la maquinaP en formato texto
    private PrintWriter archivoCodigo; //atributo para generar el archivo de codigo de maquina P
    private Token token; //token actual que estamos procesando
    private Hashtable palRes; //tabla de palabras reservadas

    /*Constructor que recibira la instancia  del analizador lexico y la ruta del
     fichero destino del lenguaje objeto*/
    public AnalizadorSintactico(AnalizadorLexico lex, String ficheroDestino) throws Exception {
        this.anLex = lex;
        this.archivoCodigo = new PrintWriter(ficheroDestino);
        this.err = new ArrayList<String>();
        this.cod = "";
        this.codDebug = "";
        token = anLex.sigToken(); //cogemos el primer token
        rellenarTablaPalRes(); //rellenamos nuestra tabla de palabras reservadas
    }

    /*Metodo que inicia el analisis sintactico*/
    public void analisisSintactico() throws Exception {
        Prog();
    }

    /*Metodos que implementan la traduccion predictivo-recursivo*/
    private void Prog() throws Exception {
        Decs();
        ckToken(CatLexica.SEP);
        Ins();
        emite("111111");
        codDebug += "stop\n";
        archivoCodigo.print(cod); //al final del todo llenamos el archivo de codigo
        archivoCodigo.close();
        System.out.println("Codigo generado de la maquina p:");
        System.out.println(cod);
        System.out.println("Codigo generado en texto: ");
        System.out.println(codDebug);
    }

    private AtributosSintact Decs() throws Exception {
        AtributosSintact atrib = Dec();
        atrib.setDir(0);
        ts = new Hashtable();
        ponID(atrib.getId(), atrib);
        dir = 1;
        return RD();        
    }

    private AtributosSintact RD() throws Exception {
        if (token.getCat() == CatLexica.PYCOMA) {
            ckToken(CatLexica.PYCOMA);
            AtributosSintact atrib = Dec();
            if (existeID(atrib.getId())) {
                this.err.add("Linea "+ (token.getNumLinea() - 1)  + " , Columna " + token.getNumCol() +  ": Variable " + atrib.getId() + " ya declarada anteriormente");
            }
            else {
                atrib.setDir(dir);
                ponID(atrib.getId(), atrib);
                dir += 1;
            }
            return RD();
        }
        return new AtributosSintact();
    }

    private AtributosSintact Dec() throws Exception {
        AtributosSintact dec = new AtributosSintact();
        if (esPalabraReservada(token.getLex())){
            err.add("Linea "+ (token.getNumLinea()) + " , Columna " + token.getNumCol() + ": Identificador '" + token.getLex() + "' no valido, no se pueden usar palabras reservadas");
            token = this.anLex.sigToken();
            ckToken(CatLexica.DOS_PUNTOS);
            dec.setTipo(AtributosSintact.tiposPerm.ERR);
            token = this.anLex.sigToken();
        }
        else {
            dec.setId(token.getLex());
            ckToken(CatLexica.IDENT);
            ckToken(CatLexica.DOS_PUNTOS);
            AtributosSintact tipo = Tipo();
            dec.setTipo(tipo.getTipo());
            if (tipo.getTipo() == AtributosSintact.tiposPerm.INTEGER) {
                ckToken(CatLexica.INTEGER);
            } else if (tipo.getTipo() == AtributosSintact.tiposPerm.NATURAL) {
                ckToken(CatLexica.NATURAL);
            } else if (tipo.getTipo() == AtributosSintact.tiposPerm.FLOAT) {
                ckToken(CatLexica.FLOAT);
            } else if (tipo.getTipo() == AtributosSintact.tiposPerm.BOOLEAN) {
                ckToken(CatLexica.BOOLEAN);
            } else if (tipo.getTipo() == AtributosSintact.tiposPerm.CHAR) {
                ckToken(CatLexica.CHAR);
            } else if (tipo.getTipo() == AtributosSintact.tiposPerm.ERR) {
                token = anLex.sigToken();
            }
        }
        return dec;
    }

    private AtributosSintact Ins() throws Exception {
        I();
        return RI();
    }

    private AtributosSintact I() throws Exception {
        switch (token.getCat()) {
            case IDENT:
                return IAsig();
            case IN:
                return ILect();
            case OUT:
                return IEsc();
            default:
                throw new Exception("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Error de sintaxis, token no esperado\n");
        }
    }

    private AtributosSintact RI() throws Exception {
        if (token.getCat() == CatLexica.PYCOMA) {
            ckToken(CatLexica.PYCOMA);
            I();
            return RI();
        }
        return new AtributosSintact();
    }

    private AtributosSintact ILect() throws Exception {
        AtributosSintact iLect = new AtributosSintact();
        ckToken(CatLexica.IN);
        ckToken(CatLexica.PAP);
        if(!existeID(token.getLex())) {
            err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Variable '" + token.getLex() + "' inexistente");
        }
        String lexemaIden = token.getLex();
        AtributosSintact iden = (AtributosSintact) ts.get(lexemaIden);
        int dirIden = iden.getDir();
        ckToken(CatLexica.IDENT);
        ckToken(CatLexica.PCIE);
        emite("110100");
        codDebug += "leer\n";
        emite("000010 " + dirIden);
        codDebug += "desapila_dir(" + dirIden + ")\n";
        return iLect;
    }

    private AtributosSintact IEsc() throws Exception {
        AtributosSintact iEsc = new AtributosSintact();
        ckToken(CatLexica.OUT);
        ckToken(CatLexica.PAP);
        AtributosSintact exp = Exp0();
        if(exp.getTipo()== AtributosSintact.tiposPerm.ERR) {
            err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Expresion erronea");
        }
        ckToken(CatLexica.PCIE);
        emite("110101");
        codDebug += "escribir\n";
        return iEsc;
    }

    private AtributosSintact IAsig() throws Exception {
        AtributosSintact iAsig = new AtributosSintact();
        String idlex = token.getLex();
        if(!existeID(idlex)) {
            err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Variable '" + token.getLex() + "' inexistente");
            ckToken(CatLexica.IDENT);
            ckToken(CatLexica.ASIG);
            Exp0();
            return iAsig;
        }
        else {
            AtributosSintact iden = (AtributosSintact) ts.get(idlex);
            int dirIden = iden.getDir();
            ckToken(CatLexica.IDENT);
            ckToken(CatLexica.ASIG);
            AtributosSintact exp = Exp0();
            if (iden.getTipo() != exp.getTipo()){
                if (exp.getTipo() == AtributosSintact.tiposPerm.NATURAL &&
                   (iden.getTipo() != AtributosSintact.tiposPerm.INTEGER &&
                    iden.getTipo() != AtributosSintact.tiposPerm.FLOAT)) {
                        err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() +  ": Tipos incompatibles");
                }
                else {
                    if (exp.getTipo() == AtributosSintact.tiposPerm.INTEGER &&
                        iden.getTipo() != AtributosSintact.tiposPerm.FLOAT) {
                        err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() +  ": Tipos incompatibles");
                    }
                    else {
                        if (exp.getTipo() == AtributosSintact.tiposPerm.FLOAT) {
                            err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() +  ": Tipos incompatibles");
                        }
                        else {
                            if (exp.getTipo() == AtributosSintact.tiposPerm.BOOLEAN || exp.getTipo() == AtributosSintact.tiposPerm.CHAR) {
                                err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() +  ": Tipos incompatibles");
                            }
                        }
                    }
                }
            }
            emite("000010 "+ dirIden);
            codDebug += "desapila_dir("+ dirIden + ")\n";
            return iAsig;
        }
    }

    private AtributosSintact Exp0() throws Exception {
       AtributosSintact exp0 = new AtributosSintact();
       AtributosSintact exp1 = Exp1();
       AtributosSintact r0 = R0(exp1);
       exp0.setTipo(r0.getTipo());
       return exp0;
    }

    private AtributosSintact R0(AtributosSintact h) throws Exception {
        AtributosSintact r0 = new AtributosSintact();
        CatLexica c = token.getCat();
        if (c.equals(CatLexica.MENOR) ||c.equals(CatLexica.MAYOR) ||c.equals(CatLexica.MENOR_IGUAL)
                ||c.equals(CatLexica.MAYOR_IGUAL) ||c.equals(CatLexica.IGUAL) ||c.equals(CatLexica.DISTINTO)) {
            AtributosSintact op = opNivel0(c);
            token = anLex.sigToken();
            AtributosSintact exp1 = Exp1();
            AtributosSintact.tiposPerm tipo1 = exp1.getTipo();
            AtributosSintact.tiposPerm tipo = tipoOpNivel0(h.getTipo(),tipo1);
            r0.setTipo(tipo);
            emite(op.getCod());
            codDebug += op.getCodString() + "\n";
        }
        else { //Lambda
            r0.setTipo(h.getTipo());
        }
        return r0;
    }

    private AtributosSintact Exp1() throws Exception {
       AtributosSintact exp1 = new AtributosSintact();
       AtributosSintact exp2 = Exp2();
       AtributosSintact r1 = R1(exp2);
       exp1.setTipo(r1.getTipo());
       return exp1;
    }

    private AtributosSintact R1(AtributosSintact h) throws Exception {
        AtributosSintact r1 = new AtributosSintact();
        CatLexica c = token.getCat();
        if (c.equals(CatLexica.MAS) ||c.equals(CatLexica.MENOS) ||c.equals(CatLexica.OR)) {
            AtributosSintact opniv1 = opNivel1(c);
            token = anLex.sigToken();            
            AtributosSintact exp2 = Exp2();
            AtributosSintact.tiposPerm tipo1 = exp2.getTipo();
            AtributosSintact.tiposPerm tipo2 = tipoOpNivel1(h.getTipo(), tipo1, opniv1);
            AtributosSintact tip2 = new AtributosSintact();
            tip2.setTipo(tipo2);
            AtributosSintact tipo3 = R1(tip2);
            r1.setTipo(tipo3.getTipo());
            emite(opniv1.getCod());
            codDebug += opniv1.getCodString() + "\n";
        }
        else { //Lambda
            r1.setTipo(h.getTipo());
        }
        return r1;
    }

    private AtributosSintact Exp2() throws Exception {
       AtributosSintact exp2 = new AtributosSintact();
       AtributosSintact exp3 = Exp3();
       AtributosSintact r2 = R2(exp3);
       exp2.setTipo(r2.getTipo());
       return exp2;
    }

    private AtributosSintact R2(AtributosSintact h) throws Exception {
        AtributosSintact r2 = new AtributosSintact();
        CatLexica c = token.getCat();
        if (c.equals(CatLexica.MULT) ||c.equals(CatLexica.DIV) ||c.equals(CatLexica.MOD)
                || c.equals(CatLexica.AND)) {
            AtributosSintact opniv2 = opNivel2(c);
            token = anLex.sigToken();
            AtributosSintact exp3 = Exp3();
            AtributosSintact.tiposPerm tipo1 = exp3.getTipo();
            AtributosSintact.tiposPerm tipo2 = tipoOpNivel2(h.getTipo(), tipo1, opniv2);
            AtributosSintact tip2 = new AtributosSintact();
            tip2.setTipo(tipo2);
            AtributosSintact tipo3 = R2(tip2);
            r2.setTipo(tipo3.getTipo());
            emite(opniv2.getCod());
            codDebug += opniv2.getCodString() + "\n";
        }
        else { //Lambda
            r2.setTipo(h.getTipo());
        }
        return r2;
    }

    private AtributosSintact Exp3() throws Exception {
       AtributosSintact exp3 = new AtributosSintact();
       AtributosSintact exp4 = Exp4();
       AtributosSintact r3 = R3(exp4);
       exp3.setTipo(r3.getTipo());
       return exp3;
    }

    private AtributosSintact R3(AtributosSintact h) throws Exception {
        AtributosSintact r3 = new AtributosSintact();
        CatLexica c = token.getCat();
        if (c.equals(CatLexica.DESP_DER) ||c.equals(CatLexica.DESP_IZQ)) {
            AtributosSintact opniv3 = opNivel3(c);
            token = anLex.sigToken();
            AtributosSintact exp4 = Exp4();
            AtributosSintact.tiposPerm tipo1 = exp4.getTipo();
            AtributosSintact.tiposPerm tipo = tipoOpNivel3(h.getTipo(),tipo1);
            r3.setTipo(tipo);
            emite(opniv3.getCod());
            codDebug += opniv3.getCodString() + "\n";
        }
        else { //Lambda
            r3.setTipo(h.getTipo());
        }
        return r3;
    }

    private AtributosSintact Exp4() throws Exception {
        AtributosSintact exp40 = new AtributosSintact();
        CatLexica c = token.getCat();
        if (c == CatLexica.BARRA) {
            ckToken(CatLexica.BARRA);
            AtributosSintact exp41 = Exp4();
            ckToken(CatLexica.BARRA);
            exp40.setTipo(exp41.getTipo());
            emite("011000");
            codDebug += "absoluto\n";
        }
        else {
            if (c == CatLexica.NOT || c == CatLexica.MENOS) {
                AtributosSintact op4a = opNivel4a(c);
                token = anLex.sigToken();
                AtributosSintact exp41 = Exp4();
                exp40.setTipo(tipoOpNivel4a(exp41.getTipo(),op4a));
                emite(op4a.getCod());
                codDebug += op4a.getCodString() + "\n";
            }
            else {
                if (c == CatLexica.CONV_NAT || c == CatLexica.CONV_INT ||
                    c == CatLexica.CONV_FLOAT || c == CatLexica.CONV_CHAR) {
                    AtributosSintact op4b = opNivel4b(c);
                    token = anLex.sigToken();
                    AtributosSintact exp5 = Exp5();
                    exp40.setTipo(tipoOpNivel4b(exp5.getTipo(),op4b));
                    emite(op4b.getCod());
                    codDebug += op4b.getCodString() + "\n";
                }
                else {
                    AtributosSintact exp5 = Exp5();
                    exp40.setTipo(exp5.getTipo());
                }
            }
        }
        return exp40;
    }

    private AtributosSintact Exp5() throws Exception {
        AtributosSintact exp5 = new AtributosSintact();
        CatLexica c = token.getCat();
        if (c == CatLexica.PAP) {
            ckToken(CatLexica.PAP);
            AtributosSintact exp0 = Exp0();
            exp5.setTipo(exp0.getTipo());
        } else {
            if (c == CatLexica.IDENT) {
                String idlex = token.getLex();
                if (ts.containsKey(idlex)) {
                    AtributosSintact iden = (AtributosSintact) ts.get(idlex);
                    exp5.setTipo(iden.getTipo());
                    emite("000001 " + iden.getDir());
                    codDebug += "apila_dir(" + iden.getDir() + ")\n";
                } else {
                    err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Variable '" + token.getLex() + "'inexistente");
                }
            } else {
                if (c == CatLexica.TRUE || c == CatLexica.FALSE) {
                    exp5.setTipo(AtributosSintact.tiposPerm.BOOLEAN);
                    if (c == CatLexica.TRUE) {
                        emite("000000 " + "true");
                        codDebug += "apila(true)\n";
                    } else {
                        emite("000000 " + "false");
                        codDebug += "apila(false)\n";
                    }
                } else {
                    if (c == CatLexica.NUM_NAT) {
                        exp5.setTipo(AtributosSintact.tiposPerm.NATURAL);
                        emite("000000 " + token.getLex());
                        codDebug += "apila(" + token.getLex() + ")\n";
                    } else {
                        if (c == CatLexica.NUM_FLOAT) {
                            exp5.setTipo(AtributosSintact.tiposPerm.FLOAT);
                            emite("000000 " + token.getLex());
                            codDebug += "apila(" + token.getLex() + ")\n";
                        } else {
                            if (c == CatLexica.CTE_CHAR) {
                                exp5.setTipo(AtributosSintact.tiposPerm.CHAR);
                                emite("000000 " + token.getLex());
                                codDebug += "apila(" + token.getLex() + ")\n";
                            } else {
                                err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Expresion erronea");
                            }
                        }
                    }
                }
            }
        }
        if (token.getCat() != CatLexica.PCIE) {
            token = anLex.sigToken();
        } else {
            ckToken(CatLexica.PCIE);
        }
        return exp5;
    }

    private AtributosSintact opNivel0(CatLexica cl) throws Exception {
        AtributosSintact op0 = new AtributosSintact();
        switch (cl) {
           case MENOR: op0.setCod("100010"); op0.setCodString("menor"); break;
           case MAYOR: op0.setCod("100011"); op0.setCodString("mayor"); break;
           case MENOR_IGUAL: op0.setCod("100100"); op0.setCodString("menorigual"); break;
           case MAYOR_IGUAL: op0.setCod("100101"); op0.setCodString("mayorigual"); break;
           case IGUAL: op0.setCod("100111"); op0.setCodString("igual"); break;
           case DISTINTO: op0.setCod("100110"); op0.setCodString("distinto"); break;
        }
        return op0;
    }

    private AtributosSintact opNivel1(CatLexica cl) throws Exception {
        AtributosSintact op1 = new AtributosSintact();
        switch (cl) {
           case MAS: op1.setCod("010000"); op1.setCodString("suma"); break;
           case MENOS: op1.setCod("010001"); op1.setCodString("resta"); break;
           case OR: op1.setCod("100001"); op1.setCodString("or"); break;
        }
        return op1;
    }

    private AtributosSintact opNivel2(CatLexica cl) throws Exception {
        AtributosSintact op2 = new AtributosSintact();
        switch (cl) {
            case MULT: op2.setCod("010010"); op2.setCodString("multiplica"); break;
            case DIV: op2.setCod("010011"); op2.setCodString("divide"); break;
            case MOD: op2.setCod("010100"); op2.setCodString("modulo"); break;
            case AND: op2.setCod("100000"); op2.setCodString("and"); break;
        }
        return op2;
    }

    private AtributosSintact opNivel3(CatLexica cl) throws Exception {
        AtributosSintact op3 = new AtributosSintact();
        if (cl == CatLexica.DESP_DER) {
            op3.setCod("010101");
            op3.setCodString("despDer");
        }
        else if (cl == CatLexica.DESP_IZQ) {
            op3.setCod("010110");
            op3.setCodString("despIzq");
        }
        return op3;
    }

    private AtributosSintact opNivel4a(CatLexica cl) throws Exception {
        AtributosSintact op4a = new AtributosSintact();
        if (cl == CatLexica.NOT) {
            op4a.setCod("101000");
            op4a.setCodString("negacion");
        }
        else if (cl == CatLexica.MENOS) {
            op4a.setCod("010111");
            op4a.setCodString("cambiarSigno");
        }
        return op4a;
    }

    private AtributosSintact opNivel4b(CatLexica cl) throws Exception {
        AtributosSintact op4b = new AtributosSintact();
        switch (cl) {
            case CONV_NAT: 
                op4b.setCod("110010");
                op4b.setTipo(AtributosSintact.tiposPerm.NATURAL);
                op4b.setCodString("castNat");
                break;
            case CONV_INT: 
                op4b.setCod("110001");
                op4b.setTipo(AtributosSintact.tiposPerm.INTEGER);
                op4b.setCodString("castInt");
                break;
            case CONV_FLOAT: 
                op4b.setCod("110000");
                op4b.setTipo(AtributosSintact.tiposPerm.FLOAT);
                op4b.setCodString("castFloat");
                break;
            case CONV_CHAR: 
                op4b.setCod("110011");
                op4b.setTipo(AtributosSintact.tiposPerm.CHAR);
                op4b.setCodString("castChar");
                break;
        }
        return op4b;
    }

    private AtributosSintact.tiposPerm tipoOpNivel0(AtributosSintact.tiposPerm t1, AtributosSintact.tiposPerm t2) throws Exception {
        if ((t1 == t2) && (t1!=AtributosSintact.tiposPerm.ERR)) {
            return AtributosSintact.tiposPerm.BOOLEAN;
        }
        else {
            if ((t1 != AtributosSintact.tiposPerm.BOOLEAN && t1 != AtributosSintact.tiposPerm.CHAR) &&
                    (t2 != AtributosSintact.tiposPerm.BOOLEAN && t2 != AtributosSintact.tiposPerm.CHAR)) {
                return AtributosSintact.tiposPerm.BOOLEAN;
            }
            else {
                err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Tipos no compatibles");
                return AtributosSintact.tiposPerm.ERR;
            }
        }
    }

    private AtributosSintact.tiposPerm tipoOpNivel1(AtributosSintact.tiposPerm t1, AtributosSintact.tiposPerm t2, AtributosSintact o) throws Exception {
        if (o.getCod().equals("or")) {
            if ((t1 == t2) && (t1==AtributosSintact.tiposPerm.BOOLEAN))
                return AtributosSintact.tiposPerm.BOOLEAN;
            else {
                err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Expresion no booleana");
                return AtributosSintact.tiposPerm.ERR;
            }
        }
        else {
            if (t1 == AtributosSintact.tiposPerm.ERR || t2 == AtributosSintact.tiposPerm.ERR ||
                    t1 == AtributosSintact.tiposPerm.BOOLEAN || t2 == AtributosSintact.tiposPerm.BOOLEAN ||
                    t1 == AtributosSintact.tiposPerm.CHAR || t2 == AtributosSintact.tiposPerm.CHAR) {
                err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Tipos no compatibles");
                return AtributosSintact.tiposPerm.ERR;
            }
            else {
                if (t1 == AtributosSintact.tiposPerm.FLOAT || t2 == AtributosSintact.tiposPerm.FLOAT) {
                    return AtributosSintact.tiposPerm.FLOAT;
                }
                else {
                    if (t1 == AtributosSintact.tiposPerm.INTEGER || t2 == AtributosSintact.tiposPerm.INTEGER) {
                        return AtributosSintact.tiposPerm.INTEGER;
                    }
                    else {
                        return AtributosSintact.tiposPerm.NATURAL;
                    }
                }
            }
        }
    }

    private AtributosSintact.tiposPerm tipoOpNivel2(AtributosSintact.tiposPerm t1, AtributosSintact.tiposPerm t2, AtributosSintact o) throws Exception {
        if (o.getCod().equals("and")) {
            if ((t1 == t2) && (t1==AtributosSintact.tiposPerm.BOOLEAN))
                return AtributosSintact.tiposPerm.BOOLEAN;
            else {
                err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Expresion no booleana");
                return AtributosSintact.tiposPerm.ERR;
            }
        }
        else {
            if (o.getCod().equals("modulo")) {
                if (t2 != AtributosSintact.tiposPerm.NATURAL) {
                    err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": El segundo operando debe ser un natural");
                    return AtributosSintact.tiposPerm.ERR;
                }
                else {
                    if (t1 != AtributosSintact.tiposPerm.NATURAL && t1 != AtributosSintact.tiposPerm.INTEGER) {
                        err.add("Linea "+ token.getNumLinea() + " , Columna " + token.getNumCol() + ": El primer operando debe ser entero o natural");
                        return AtributosSintact.tiposPerm.ERR;
                    }
                    else {
                        return t1;
                    }
                }
            }
            else {
                if (t1 == AtributosSintact.tiposPerm.ERR || t2 == AtributosSintact.tiposPerm.ERR ||
                        t1 == AtributosSintact.tiposPerm.BOOLEAN || t2 == AtributosSintact.tiposPerm.BOOLEAN ||
                        t1 == AtributosSintact.tiposPerm.CHAR || t2 == AtributosSintact.tiposPerm.CHAR) {
                    err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Tipos no compatibles");
                    return AtributosSintact.tiposPerm.ERR;
                }
                else {
                    if (t1 == AtributosSintact.tiposPerm.FLOAT || t2 == AtributosSintact.tiposPerm.FLOAT) {
                        return AtributosSintact.tiposPerm.FLOAT;
                    }
                    else {
                        if (t1 == AtributosSintact.tiposPerm.INTEGER || t2 == AtributosSintact.tiposPerm.INTEGER) {
                            return AtributosSintact.tiposPerm.INTEGER;
                        }
                        else {
                            return AtributosSintact.tiposPerm.NATURAL;
                        }
                    }
                }
            }
        }
    }

    private AtributosSintact.tiposPerm tipoOpNivel3(AtributosSintact.tiposPerm t1, AtributosSintact.tiposPerm t2) throws Exception {
        if (t1 == t2 && t1 == AtributosSintact.tiposPerm.NATURAL)
            return t1;
        else {
            err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Los operandos deben ser naturales");
            return AtributosSintact.tiposPerm.ERR;
        }
    }

    private AtributosSintact.tiposPerm tipoOpNivel4a(AtributosSintact.tiposPerm t1, AtributosSintact as) throws Exception {
        if (as.getCod().equals("negacion")) {
            if (t1 == AtributosSintact.tiposPerm.BOOLEAN) {
                return t1;
            }
            else {
                err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": El operando debe ser booleano");
                return AtributosSintact.tiposPerm.ERR;
            }
        }
        else {//Es un cambio de signo
            if (t1 == AtributosSintact.tiposPerm.ERR || t1 == AtributosSintact.tiposPerm.BOOLEAN ||
                t1 == AtributosSintact.tiposPerm.CHAR) {
                err.add("Linea "+ token.getNumLinea() + " , Columna " + token.getNumCol() + ": El operando debe ser numerico");
                return AtributosSintact.tiposPerm.ERR;
            }
            else {
                if (t1 == AtributosSintact.tiposPerm.FLOAT)
                    return t1;
                else return AtributosSintact.tiposPerm.INTEGER;
            }
        }
    }

    private AtributosSintact.tiposPerm tipoOpNivel4b(AtributosSintact.tiposPerm t1, AtributosSintact as) throws Exception {
        if (t1 == AtributosSintact.tiposPerm.BOOLEAN) {
            err.add("Linea "+ token.getNumLinea() + " , Columna " + token.getNumCol() + ": No se admiten booleanos");
            return AtributosSintact.tiposPerm.ERR;
        }
        else {
            if (as.getTipo() == AtributosSintact.tiposPerm.NATURAL || as.getTipo() == AtributosSintact.tiposPerm.CHAR) {
                if (t1 == AtributosSintact.tiposPerm.INTEGER || t1 == AtributosSintact.tiposPerm.FLOAT) {
                    err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": No se admiten reales o enteros");
                    return AtributosSintact.tiposPerm.ERR;
                }
                else {
                    return as.getTipo();
                }
            }
            else {
                return as.getTipo();
            }
        }
    }


    /*Metodo que comprueba que el token siguiente sea el esperado y que devuelve
     * error en caaso de que no sea correcto
     */
    private void ckToken(CatLexica tk) throws Exception {
        if (token.getCat() != CatLexica.EOF) {
            if (tk == token.getCat()) {
                token = anLex.sigToken(); //avanzamos al siguiente token
            } else {
                throw new Exception("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Error de sintaxis, token no esperado\n");
            }
        }
    }

    /*Funcion que concatena instrucciones de la maquina P para escribirlas al
     final en el archivo destino*/
    private void emite(String instruccion) {
        cod += instruccion + "\n";
    }

    /*Funcion que comprueba si ya existe el id en la tabla de simbolos */
    private boolean existeID(String id) {
        return ts.containsKey(id);
    }

    /*Metodo que introduce un nuevo idd en la tabla de simbolos*/
    private void ponID(String id,AtributosSintact atrib) {
        ts.put(id, atrib);
    }

    /*Funcion que comprueba si un lexema dado es una palabra reservada y no
     se podra usar*/
    private boolean esPalabraReservada(String lex) {
        String aux = lex.toUpperCase();
        if (palRes.containsKey(aux)) {
            return true;
        }
        return false;
    }

    //Metodo que devuelve el tipo del siguiente token.
    private AtributosSintact Tipo() {
        AtributosSintact tipo = new AtributosSintact();

        CatLexica actual = token.getCat();

        if (actual == CatLexica.INTEGER){
            tipo.setTipo(AtributosSintact.tiposPerm.INTEGER);
        }
        else if (actual == CatLexica.NATURAL){
            tipo.setTipo(AtributosSintact.tiposPerm.NATURAL);
        }
        else if (actual == CatLexica.FLOAT){
            tipo.setTipo(AtributosSintact.tiposPerm.FLOAT);            
        }
        else if (actual == CatLexica.BOOLEAN){
            tipo.setTipo(AtributosSintact.tiposPerm.BOOLEAN);
        }
        else if (actual == CatLexica.CHAR){
            tipo.setTipo(AtributosSintact.tiposPerm.CHAR);            
        }
        else {
            err.add("Linea " + token.getNumLinea() + " , Columna " + token.getNumCol() + ": Tipo no permitido");
            tipo.setTipo(AtributosSintact.tiposPerm.ERR);            
        }

        return tipo;
    }

    /*Metodo que rellena una tabla hash con las palabras reservadas de modo
     que sea sencillo comprobar si un lexema es palabra reservada*/
    private void rellenarTablaPalRes() {
        palRes = new Hashtable();
        palRes.put("NATURAL", 0);
        palRes.put("INTEGER", 0);
        palRes.put("FLOAT", 0);
        palRes.put("BOOLEAN", 0);
        palRes.put("TRUE", 0);
        palRes.put("FALSE", 0);
        palRes.put("CHAR", 0);
        palRes.put("AND", 0);
        palRes.put("OR", 0);
        palRes.put("NOT", 0);
        palRes.put("IN", 0);
        palRes.put("OUT", 0);
        palRes.put("&", 0);
    }

    public ArrayList<String> getErr() {
        return err;
    }
}
